﻿using LogLib;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;

namespace PixelDetectSDKInterface
{
    /// <summary>
    /// Pixel处理和检测类
    /// </summary>
    public class PixelDetectSDK : IDisposable
    {
        private byte[] ImageByteArray = null;

        private int ArrayDataLength = 0;

        private IntPtr _stdDataPtr = IntPtr.Zero;
        private bool _inited = false;
        public bool Inited
        {
            get { return _inited; }
        }

        private DetectHandleResult _detectResult = new DetectHandleResult();
        public DetectHandleResult DetectResult
        {
            get { return _detectResult; }
        }

        private IntPtr _handle = IntPtr.Zero;

        private string _stdFile = "";

        public PixelDetectSDK(string stdFile)
        {
            _stdFile = stdFile;
        }

        public bool Init()
        {
            string directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);
            directory = directory.Substring(6);
            string modelsPath = Path.Combine(directory, "models");
            int ret = DetectSDKInterface.CreateImageDetectHandle(ref _handle, modelsPath);
            if (ret != 0)
            {
                _detectResult.TakeError(ret);
                return false;
            }
            if (!LoadSTD())
            {
                return false;
            }
            _inited = true;
            return true;
        }

        public void Dispose()
        {
            if (_inited && _handle != IntPtr.Zero)
            {
                DetectSDKInterface.DestroyImageDetectHandle(_handle);
            }
        }

        private void AllocSourceMemory(string src)
        {
            ImageByteArray = ComFun.ReadByteArray(src);
            ArrayDataLength = ImageByteArray.Length;         
        }

        private void AllocSourceMemory(Bitmap srcImg)
        {
            ImageByteArray = ComFun.Bitmap2ByteArray_BMP(srcImg);
            ArrayDataLength = ImageByteArray.Length;          
        }

        private void OnCropSaveHanding(string dest)
        {
            try
            {
                if (!Load())
                {
                    return;
                }
                if (!Adjust())
                {
                    return;
                }
                if (!Save(dest))
                {
                    return;
                }
                _detectResult.SetWellDone();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }          
        }

        public void CropSave(string src, string dest)
        {
            _detectResult.Reset();
            AllocSourceMemory(src);
            OnCropSaveHanding(dest);
        }

        public void CropSave(Bitmap src, string dest)
        {
            _detectResult.Reset();
            AllocSourceMemory(src);
            OnCropSaveHanding(dest);
        }

        private void OnAllHanding(string dest)
        {
            try
            {
                if (!Load())
                {
                    return;
                }
                if (!Adjust())
                {
                    return;
                }
                if (!Save(dest))
                {
                    return;
                }
                if (!Detect())
                {
                    return;
                }
                _FGDetectAssess assess = new _FGDetectAssess();
                if (!Assess(out assess))
                {
                    return;
                }
                _detectResult.DetectAccess = assess;
                if (assess.pass > 0)
                {
                    return;
                }
                string assessString = "";
                if (!AssessString(out assessString))
                {
                    return;
                }
                _detectResult.AccessString = assessString;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        public void CropSaveDetectAssess(string src, string dest)
        {
            Bitmap srcImage = new Bitmap(src);
            _detectResult.Reset();
            AllocSourceMemory(srcImage);            
            OnAllHanding(dest);
        }

        //public void CropSaveDetectAssess(Bitmap src, string dest)
        //{
        //    _detectResult.Reset();
        //    AllocSourceMemory(src);
        //    OnAllHanding(dest);
        //}

        public void DetectAssess(string src, out _FGDetectAssess assess)
        {
            assess = new _FGDetectAssess();
            _detectResult.Reset();
            AllocSourceMemory(src);
            try
            {
                if (!Load())
                {
                    return;
                }
                if (!Detect())
                {
                    return;
                }
                if (!Assess(out assess))
                {
                    return;
                }
                _detectResult.DetectAccess = assess;
                if (assess.pass > 0)
                {
                    return;
                }
                string assessString = "";
                if (!AssessString(out assessString))
                {
                    return;
                }
                _detectResult.AccessString = assessString;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        private bool LoadSTD()
        {
            if (!File.Exists(_stdFile))
            {
                _detectResult.TakeError(-3);
                return false;
            }
            byte[] readArray = ComFun.ReadByteArray(_stdFile);          
            int ret = DetectSDKInterface.LoadStd(_handle, readArray, readArray.Length);
            if (0 == ret)
            {
                return true;
            }
            else
            {
                _detectResult.TakeError(ret);
                return false;
            }
        }

        private bool Load()
        {
            int ret = DetectSDKInterface.LoadImage(_handle, ImageByteArray, ArrayDataLength);
            if (0 == ret)
            {
                return true;
            }
            else
            {
                _detectResult.TakeError(ret);
                return false;
            }
        }

        private bool Adjust()
        {
            int flag = ((int)AdjustParam._FG_DSDK_AUTOAD_CLBK
                | (int)AdjustParam._FG_DSDK_AUTOAD_CROP);
            int ret = DetectSDKInterface.AdjustImage(_handle, flag);
            if (0 == ret)
            {
                return true;
            }
            else
            {
                _detectResult.TakeError(ret);
                return false;
            }
        }

        private bool Save(string destPath)
        {
            int size = Marshal.SizeOf(typeof(int));
            IntPtr ptr = Marshal.AllocHGlobal(size);
            int ret = DetectSDKInterface.GetImageSize(_handle, ptr, ".jpg");
            int length = Marshal.ReadInt32(ptr);
            Marshal.FreeHGlobal(ptr);
            if (ret != 0)
            {
                _detectResult.TakeError(ret);
                return false;
            }
            if (length == 0)
            {
                _detectResult.TakeError(-7);
                return false;
            }
            byte[] destDataArray = new byte[length];
            ret = DetectSDKInterface.GetImage(_handle, destDataArray, ".jpg");
            if (ret != 0)
            {
                _detectResult.TakeError(ret);
                return false;
            }
            MemoryStream msTmp = new MemoryStream((byte[])destDataArray); ;
            Bitmap cropImg = new Bitmap(msTmp);
            try
            {
                cropImg.Save(destPath, ImageFormat.Jpeg);
                return true;
            }
            catch (Exception ex)
            {
                _detectResult.TakeException(ex);
                return false;
            }
            finally
            {
                msTmp.Close();
                msTmp.Dispose();
                msTmp = null;
                cropImg.Dispose();
                cropImg = null;
                destDataArray = null;
            }
        }

        private bool Detect()
        {
            int ret = DetectSDKInterface.DetectImage(_handle);
            if (0 != ret)
            {
                _detectResult.TakeError(ret);
                return false;
            }
            return true;
        }

        private bool Assess(out _FGDetectAssess assess)
        {
            assess = new _FGDetectAssess();
            int size = Marshal.SizeOf(typeof(_FGDetectAssess));
            IntPtr ptr = Marshal.AllocHGlobal(size);
            int ret = DetectSDKInterface.Assess(_handle, ptr);
            if (0 != ret)
            {
                Marshal.FreeHGlobal(ptr);
                _detectResult.TakeError(ret);
                return false;
            }
            assess = (_FGDetectAssess)Marshal.PtrToStructure(ptr, typeof(_FGDetectAssess));
            Marshal.FreeHGlobal(ptr);
            return true;
        }

        public static int Assess(IntPtr hDetect, out _FGDetectAssess assess)
        {
            assess = new _FGDetectAssess();
            int size = Marshal.SizeOf(typeof(_FGDetectAssess));
            IntPtr ptr = Marshal.AllocHGlobal(size);
            int ret = DetectSDKInterface.Assess(hDetect, ptr);
            if (0 != ret)
            {
                Marshal.FreeHGlobal(ptr);
                return ret;
            }
            assess = (_FGDetectAssess)Marshal.PtrToStructure(ptr, typeof(_FGDetectAssess));
            Marshal.FreeHGlobal(ptr);
            return ret;
        }

        private bool AssessString(out string assessString)
        {
            assessString = "";
            int length = 128;
            IntPtr ptr = Marshal.AllocHGlobal(length);
            //byte[] byteArray = new byte[length];
            int ret = DetectSDKInterface.AssessString(_handle, length, ptr);
            if (0 != ret)
            {
                Marshal.FreeHGlobal(ptr);
                _detectResult.TakeError(ret);
                return false;
            }
            assessString = Marshal.PtrToStringAnsi(ptr);
            Marshal.FreeHGlobal(ptr);
            return true;
        }

        public static int AssessString(IntPtr hDetect, out string assessString)
        {
            assessString = "";
            int length = 256;
            IntPtr ptr = Marshal.AllocHGlobal(length);
            int ret = DetectSDKInterface.AssessString(hDetect, length, ptr);
            if (0 != ret)
            {
                Marshal.FreeHGlobal(ptr);
                return ret;
            }
            assessString = Marshal.PtrToStringAnsi(ptr);
            Marshal.FreeHGlobal(ptr);
            return ret;
        }

        public bool DetectFaceLocationAttr(string src, out _FGFaceLocationAttribute attr)
        {
            _detectResult.Reset();
            AllocSourceMemory(src);
            attr = new _FGFaceLocationAttribute();
            try
            {
                if (!Load())
                {
                    return false;
                }
                int size = Marshal.SizeOf(typeof(_FGFaceLocationAttribute));
                IntPtr ptr = Marshal.AllocHGlobal(size);
                int ret = DetectSDKInterface.PixelDetectFaceLocationAttribute(_handle, ptr);
                if (0 != ret)
                {
                    Marshal.FreeHGlobal(ptr);
                    _detectResult.TakeError(ret);
                    return false;
                }
                attr = (_FGFaceLocationAttribute)Marshal.PtrToStructure(ptr, typeof(_FGFaceLocationAttribute));
                Marshal.FreeHGlobal(ptr);
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return false;
            }         
        }

        public bool DetectFacePose(string src, out _FGFaceAttribute attr)
        {
            _detectResult.Reset();
            AllocSourceMemory(src);
            attr = new _FGFaceAttribute();
            try
            {
                if (!Load())
                {
                    return false;
                }
                int size = Marshal.SizeOf(typeof(_FGFaceAttribute));
                IntPtr ptr = Marshal.AllocHGlobal(size);
                int ret = DetectSDKInterface.PixelDetectFaceAttribute(_handle, ptr);
                if (0 != ret)
                {
                    Marshal.FreeHGlobal(ptr);
                    _detectResult.TakeError(ret);
                    return false;
                }
                attr = (_FGFaceAttribute)Marshal.PtrToStructure(ptr, typeof(_FGFaceAttribute));
                Marshal.FreeHGlobal(ptr);
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return false;
            }
        }
   
        public int GetStdData(out _FGImageDetectStdData stdData)
        {
            return GetStdData(_handle, out stdData);
        }

        public static int GetStdData(IntPtr hDetect, out _FGImageDetectStdData stdData)
        {
            stdData = new _FGImageDetectStdData();
            try
            {
                int size = Marshal.SizeOf(typeof(_FGImageDetectStdData));
                IntPtr ptr = Marshal.AllocHGlobal(size);
                int ret = DetectSDKInterface.PixelDetectGetStdData(hDetect, ptr);
                if (0 != ret)
                {
                    Marshal.FreeHGlobal(ptr);
                    return ret;
                }
                stdData = (_FGImageDetectStdData)Marshal.PtrToStructure(ptr, typeof(_FGImageDetectStdData));
                Marshal.FreeHGlobal(ptr);
                return 0;
            }
            catch (Exception ex)
            {
                Log.WriteException(ex.ToString());
                return -99;
            }
        }

        public static int DetectFacePose(IntPtr hDetect, byte[] pData, int nSize, out _FGFaceAttribute attr)
        {
            attr = new _FGFaceAttribute();
            try
            {
                int ret = DetectSDKInterface.LoadImage(hDetect, pData, nSize);
                if (0 != ret)
                {
                    return ret;
                }
                int size = Marshal.SizeOf(typeof(_FGFaceAttribute));
                IntPtr ptr = Marshal.AllocHGlobal(size);
                ret = DetectSDKInterface.PixelDetectFaceAttribute(hDetect, ptr);
                if (0 != ret)
                {
                    Marshal.FreeHGlobal(ptr);
                    return ret;
                }
                attr = (_FGFaceAttribute)Marshal.PtrToStructure(ptr, typeof(_FGFaceAttribute));
                Marshal.FreeHGlobal(ptr);
                return 0;
            }
            catch (Exception ex)
            {
                Log.WriteException(ex.ToString());
                return -99;
            }
        }

        public static int DetectFaceLocation(IntPtr hDetect, byte[] pData, int nSize, out _FGFaceLocationAttribute attr)
        {
            attr = new _FGFaceLocationAttribute();
            try
            {
                int ret = DetectSDKInterface.LoadImage(hDetect, pData, nSize);
                if (0 != ret)
                {
                    return ret;
                }
                int size = Marshal.SizeOf(typeof(_FGFaceLocationAttribute));
                IntPtr ptr = Marshal.AllocHGlobal(size);
                ret = DetectSDKInterface.PixelDetectFaceLocationAttribute(hDetect, ptr);
                if (0 != ret)
                {
                    Marshal.FreeHGlobal(ptr);
                    return ret;
                }
                attr = (_FGFaceLocationAttribute)Marshal.PtrToStructure(ptr, typeof(_FGFaceLocationAttribute));
                Marshal.FreeHGlobal(ptr);
                return 0;
            }
            catch (Exception ex)
            {
                Log.WriteException(ex.ToString());
                return -99;
            }
        }

    }
}
